#include <unistd.h>
#include <platform.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <sys/resource.h>
#include "platform.h"
#include "udk_os.h"

 Mutex::Mutex() {
    pthread_mutex_init(&mMutex, NULL);
}
 Mutex::Mutex(const char* name) {
    pthread_mutex_init(&mMutex, NULL);
}
 Mutex::Mutex(int type, const char* name) {
    if (type == SHARED) {
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
        pthread_mutex_init(&mMutex, &attr);
        pthread_mutexattr_destroy(&attr);
    } else {
        pthread_mutex_init(&mMutex, NULL);
    }
}
 Mutex::~Mutex() {
    pthread_mutex_destroy(&mMutex);
}
 status_t Mutex::lock() {
    return -pthread_mutex_lock(&mMutex);
}
 void Mutex::unlock() {
    pthread_mutex_unlock(&mMutex);
}
 status_t Mutex::tryLock() {
    return -pthread_mutex_trylock(&mMutex);
}

 Condition::Condition() {
    pthread_cond_init(&mCond, NULL);
}
 Condition::Condition(int type) {
    if (type == SHARED) {
        pthread_condattr_t attr;
        pthread_condattr_init(&attr);
        pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
        pthread_cond_init(&mCond, &attr);
        pthread_condattr_destroy(&attr);
    } else {
        pthread_cond_init(&mCond, NULL);
    }
}
 Condition::~Condition() {
    pthread_cond_destroy(&mCond);
}
 status_t Condition::wait(Mutex& mutex) {
    return -pthread_cond_wait(&mCond, &mutex.mMutex);
}
 status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) {
#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)
    struct timespec ts;
    ts.tv_sec  = reltime/1000000000;
    ts.tv_nsec = reltime%1000000000;
    return -pthread_cond_timedwait_relative_np(&mCond, &mutex.mMutex, &ts);
#else // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE
    struct timespec ts;
#if defined(HAVE_POSIX_CLOCKS)
    clock_gettime(CLOCK_REALTIME, &ts);
#else // HAVE_POSIX_CLOCKS
    // we don't support the clocks here.
    struct timeval t;
    gettimeofday(&t, NULL);
    ts.tv_sec = t.tv_sec;
    ts.tv_nsec= t.tv_usec*1000;
#endif // HAVE_POSIX_CLOCKS
    ts.tv_sec += reltime/1000000000;
    ts.tv_nsec+= reltime%1000000000;
    if (ts.tv_nsec >= 1000000000) {
        ts.tv_nsec -= 1000000000;
        ts.tv_sec  += 1;
    }
    return -pthread_cond_timedwait(&mCond, &mutex.mMutex, &ts);
#endif // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE
}

 status_t Condition::wait(Mutex* mutex) {
    return -pthread_cond_wait(&mCond, &mutex->mMutex);
}
 status_t Condition::waitRelative(Mutex* mutex, nsecs_t reltime) {
#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)
    struct timespec ts;
    ts.tv_sec  = reltime/1000000000;
    ts.tv_nsec = reltime%1000000000;
    return -pthread_cond_timedwait_relative_np(&mCond, &mutex->mMutex, &ts);
#else // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE
    struct timespec ts;
#if defined(HAVE_POSIX_CLOCKS)
    clock_gettime(CLOCK_REALTIME, &ts);
#else // HAVE_POSIX_CLOCKS
    // we don't support the clocks here.
    struct timeval t;
    gettimeofday(&t, NULL);
    ts.tv_sec = t.tv_sec;
    ts.tv_nsec= t.tv_usec*1000;
#endif // HAVE_POSIX_CLOCKS
    //UDK_LOGD("now is <%ld.%06ld> timeout %lldns\n",ts.tv_sec,ts.tv_nsec,reltime);
    ts.tv_sec += reltime/1000000000;
    ts.tv_nsec+= reltime%1000000000;
    if (ts.tv_nsec >= 1000000000) {
        ts.tv_nsec -= 1000000000;
        ts.tv_sec  += 1;
    }
    //UDK_LOGD("timeout is <%ld.%06ld>\n",ts.tv_sec,ts.tv_nsec);
    return -pthread_cond_timedwait(&mCond, &mutex->mMutex, &ts);
#endif // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE
} 
 void Condition::signal() {
    pthread_cond_signal(&mCond);
}
 void Condition::broadcast() {
    pthread_cond_broadcast(&mCond);
}


typedef void* (*pthread_entry)(void*);

static bool gDoSchedulingGroup = false;

struct thread_data_t {
    thread_func_t   entryFunction;
    void*           userData;
    int             priority;
    char *          threadName;

    // we use this trampoline when we need to set the priority with
    // nice/setpriority.
    static int trampoline(const thread_data_t* t) {
        thread_func_t f = t->entryFunction;
        void* u = t->userData;
        int prio = t->priority;
        char * name = t->threadName;
        delete t;
        setpriority(PRIO_PROCESS, 0, prio);
		/*
        if (gDoSchedulingGroup) {
            if (prio >= PRIORITY_BACKGROUND) {
                set_sched_policy(udkGetTid(), SP_BACKGROUND);
            } else {
                set_sched_policy(udkGetTid(), SP_FOREGROUND);
            }
        }*/
        
        if (name) {
#if defined(HAVE_PRCTL)
            // Mac OS doesn't have this, and we build libutil for the host too
            int hasAt = 0;
            int hasDot = 0;
            char *s = name;
            while (*s) {
                if (*s == '.') hasDot = 1;
                else if (*s == '@') hasAt = 1;
                s++;
            }
            int len = s - name;
            if (len < 15 || hasAt || !hasDot) {
                s = name;
            } else {
                s = name + len - 15;
            }
            prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0);
#endif
            free(name);
        }
        return f(u);
    }
};

int udkCreateRawThreadEtc(thread_func_t entryFunction,
                               void *userData,
                               const char* threadName,
                               UDK_S32 threadPriority,
                               size_t threadStackSize,
                               thread_id_t *threadId)
{
    pthread_attr_t attr; 
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

#ifdef HAVE_ANDROID_OS  /* valgrind is rejecting RT-priority create reqs */
    if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) {
        // We could avoid the trampoline if there was a way to get to the
        // android_thread_id_t (pid) from pthread_t
        thread_data_t* t = new thread_data_t;
        t->priority = threadPriority;
        t->threadName = threadName ? strdup(threadName) : NULL;
        t->entryFunction = entryFunction;
        t->userData = userData;
        entryFunction = (thread_func_t)&thread_data_t::trampoline;
        userData = t;            
    }
#endif

    if (threadStackSize) {
        pthread_attr_setstacksize(&attr, threadStackSize);
    }
    
    errno = 0;
    pthread_t thread;
    int result = pthread_create(&thread, &attr,
                    (pthread_entry)entryFunction, userData);
    pthread_attr_destroy(&attr);
    if (result != 0) {
        UDK_LOGE("udkCreateRawThreadEtc failed (entry=%p, res=%d, errno=%d)\n"
             "(android threadPriority=%d)",
            entryFunction, result, errno, threadPriority);
        return 0;
    }

    if (threadId != NULL) {
        *threadId = (thread_id_t)thread; // XXX: this is not portable
    }
    return 1;
}

thread_id_t udkGetThreadId()
{
    return (thread_id_t)pthread_self();
}

static create_thread_fn gCreateThreadFn = udkCreateRawThreadEtc;

int udkCreateThread(thread_func_t fn, void* arg)
{
    return createThreadEtc(fn, arg);
}

int udkCreateThreadEtc(thread_func_t entryFunction,
                            void *userData,
                            const char* threadName,
                            UDK_S32 threadPriority,
                            size_t threadStackSize,
                            thread_id_t *threadId)
{
    return gCreateThreadFn(entryFunction, userData, threadName,
        threadPriority, threadStackSize, threadId);
}

void SetCreateThreadFunc(create_thread_fn func)
{
    gCreateThreadFn = func;
}

pid_t udkGetTid()
{
#ifdef HAVE_GETTID
    return gettid();
#else
    return getpid();
#endif
}


/*
 * This is our thread object!
 */

Thread::Thread(bool canCallJava)
    :   mCanCallJava(canCallJava),
        mThread(thread_id_t(-1)),
        mLock("Thread::mLock"),
        mStatus(NO_ERROR),
        mExitPending(false), mRunning(false)
{
}

Thread::~Thread()
{
}

status_t Thread::readyToRun()
{
    return NO_ERROR;
}

status_t Thread::run(const char* name, UDK_S32 priority, size_t stack)
{
    Mutex::Autolock _l(mLock);

    if (mRunning) {
        // thread already started
        return -ENOSYS;
    }

    // reset status and exitPending to their default value, so we can
    // try again after an error happened (either below, or in readyToRun())
    mStatus = NO_ERROR;
    mExitPending = false;
    mThread = thread_id_t(-1);
    

    mRunning = true;

    bool res;
    if (mCanCallJava) {
        res = createThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
    } else {
        res = udkCreateRawThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
    }
    
    if (res == false) {
        mStatus = UNKNOWN_ERROR;   // something happened!
        mRunning = false;
        mThread = thread_id_t(-1);
      
        return UNKNOWN_ERROR;
    }
    
    // Do not refer to mStatus here: The thread is already running (may, in fact
    // already have exited with a valid mStatus result). The NO_ERROR indication
    // here merely indicates successfully starting the thread and does not
    // imply successful termination/execution.
    return NO_ERROR;
}

int Thread::_threadLoop(void* user)
{
    Thread* const self = static_cast<Thread*>(user);

#if HAVE_ANDROID_OS
    // this is very useful for debugging with gdb
    self->mTid = gettid();
#endif

    bool first = true;

    do {
        bool result;
        if (first) {
            first = false;
            self->mStatus = self->readyToRun();
            result = (self->mStatus == NO_ERROR);

            if (result && !self->mExitPending) {
                // Binder threads (and maybe others) rely on threadLoop
                // running at least once after a successful ::readyToRun()
                // (unless, of course, the thread has already been asked to exit
                // at that point).
                // This is because threads are essentially used like this:
                //   (new ThreadSubclass())->run();
                // The caller therefore does not retain a strong reference to
                // the thread and the thread would simply disappear after the
                // successful ::readyToRun() call instead of entering the
                // threadLoop at least once.
                result = self->threadLoop();
            }
        } else {
            result = self->threadLoop();
        }

        if (result == false || self->mExitPending) {
            self->mExitPending = true;
            self->mLock.lock();
            self->mRunning = false;
            // clear thread ID so that requestExitAndWait() does not exit if
            // called by a new thread using the same thread ID as this one.
            self->mThread = thread_id_t(-1);
            self->mThreadExitedCondition.broadcast();
            self->mThread = thread_id_t(-1); // thread id could be reused
            self->mLock.unlock();
            break;
        }
        
    } while(1);
    pthread_exit(0);
    return 0;
}

void Thread::requestExit()
{
    mExitPending = true;
}

status_t Thread::requestExitAndWait()
{
    if (mThread == getThreadId()) {
        UDK_LOGW(
        "Thread (this=%p): don't call waitForExit() from this "
        "Thread object's thread. It's a guaranteed deadlock!",
        this);

        return -EWOULDBLOCK;
    }
    
    requestExit();

    Mutex::Autolock _l(mLock);
    while (mRunning == true) {
        mThreadExitedCondition.wait(mLock);
    }
    mExitPending = false;

    return mStatus;
}

bool Thread::exitPending() const
{
    return mExitPending;
}


#define GET_CLOCK_COUNT(x) gettimeofday(x, NULL)

CStatTimer::CStatTimer(){}

CStatTimer::~CStatTimer(){}

void CStatTimer::Initialize(){ 
	memset(&m_startTime, 0, sizeof(struct timeval));
	memset(&m_endTime, 0, sizeof(struct timeval));
}

struct timeval CStatTimer::GetStartTime() { return m_startTime; }
void CStatTimer::SetStartTime() { GET_CLOCK_COUNT(&m_startTime); }

struct timeval CStatTimer::GetEndTime() { return m_endTime; }
void CStatTimer::SetEndTime() { GET_CLOCK_COUNT(&m_endTime); }

UDK_U32 CStatTimer::GetMilliSeconds() { return (CalcTotalUSec() / MILLISECONDS_CONVERSION); }
UDK_U32 CStatTimer::GetMicroSeconds() { return (CalcTotalUSec()); }
UDK_U32 CStatTimer::GetSeconds() { return (CalcTotalUSec() / MICROSECONDS_CONVERSION); }

UDK_U32 CStatTimer::GetCurrentTime() 
{ 
	struct timeval tmpTime;
	GET_CLOCK_COUNT(&tmpTime); 
	return ((tmpTime.tv_sec * MICROSECONDS_CONVERSION) + tmpTime.tv_usec);
}

UDK_U32 CStatTimer::CalcTotalUSec() {
	return (((m_endTime.tv_sec - m_startTime.tv_sec) * MICROSECONDS_CONVERSION) + 
                       (m_endTime.tv_usec - m_startTime.tv_usec)); 
}


#ifndef INVALID_SOCKET
  #define INVALID_SOCKET    ~(0)
#endif

#define SOCKET_SENDFILE_BLOCKSIZE 8192
#include <stdarg.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <unistd.h>   
#include <fcntl.h>
#include <sys/ioctl.h>

#define ACCEPT(a,b,c)          accept(a,b,c)
#define CONNECT(a,b,c)         connect(a,b,c)
#define CLOSE(a)               close(a)
#define READ(a,b,c)            read(a,b,c)
#define RECV(a,b,c,d)          recv(a, (void *)b, c, d)
#define RECVFROM(a,b,c,d,e,f)  recvfrom(a, (char *)b, c, d, (sockaddr *)e, f)
#define RECV_FLAGS             MSG_WAITALL
#define SELECT(a,b,c,d,e)      select(a,b,c,d,e)
#define SEND(a,b,c,d)          send(a, (const UDK_S8 *)b, c, d)
#define SENDTO(a,b,c,d,e,f)    sendto(a, (const UDK_S8 *)b, c, d, e, f)
#define SEND_FLAGS             0
#define SENDFILE(a,b,c,d)      sendfile(a, b, c, d)
#define SET_SOCKET_ERROR(x,y)  errno=y
#define SOCKET_ERROR_INTERUPT  EINTR
#define SOCKET_ERROR_TIMEDOUT  EAGAIN
#define WRITE(a,b,c)           write(a,b,c)
#define WRITEV(a,b,c)          writev(a, b, c)
#define GETSOCKOPT(a,b,c,d,e)  getsockopt((int)a,(int)b,(int)c,(void *)d,(socklen_t *)e)
#define SETSOCKOPT(a,b,c,d,e)  setsockopt((int)a,(int)b,(int)c,(const void *)d,(int)e)
#define GETHOSTBYNAME(a)       gethostbyname((const char *)a)
#define LSEEK(a,b,c)           lseek(a,b,c)

/*---------------------------------------------------------------------------*/
/*                                                                           */
/* File Macros                                                               */
/*                                                                           */
/*---------------------------------------------------------------------------*/
#define STRUCT_STAT         struct stat
#define LSTAT(x,y)          lstat(x,y)
#define FILE_HANDLE         FILE *
#define CLEARERR(x)         clearerr(x)
#define FCLOSE(x)           fclose(x)
#define FEOF(x)             feof(x)
#define FERROR(x)           ferror(x)
#define FFLUSH(x)           fflush(x)
#define FILENO(s)           fileno(s)
#define FOPEN(x,y)          fopen(x, y)
  //#define FREAD(a,b,c,d)      fread(a, b, c, d)
#define FSTAT(s, st)        fstat(FILENO(s), st)
    //#define FWRITE(a,b,c,d)     fwrite(a, b, c, d)
#define STAT_BLK_SIZE(x)    ((x).st_blksize)


CSimpleSocket::CSimpleSocket(CSocketType nType) :
    m_socket(INVALID_SOCKET), 
    m_socketErrno(CSimpleSocket::SocketInvalidSocket), 
    m_pBuffer(NULL), m_nBufferSize(0), m_nSocketDomain(AF_INET), 
    m_nSocketType(SocketTypeInvalid), m_nBytesReceived(-1), 
    m_nBytesSent(-1), m_nFlags(0), 
    m_bIsBlocking(true),m_bIsMulticast(false)
{
    SetConnectTimeout(1, 0);
    memset(&m_stRecvTimeout, 0, sizeof(struct timeval));
    memset(&m_stSendTimeout, 0, sizeof(struct timeval));
    memset(&m_stLinger, 0, sizeof(struct linger));

    switch(nType)
    {
        //----------------------------------------------------------------------
        // Declare socket type stream - TCP
        //----------------------------------------------------------------------
        case CSimpleSocket::SocketTypeTcp: 
        {
            m_nSocketDomain = AF_INET;
            m_nSocketType = CSimpleSocket::SocketTypeTcp;
            break;
        }
        case CSimpleSocket::SocketTypeTcp6: 
        {
            m_nSocketDomain = AF_INET6;
            m_nSocketType = CSimpleSocket::SocketTypeTcp6;
            break;
        }
        //----------------------------------------------------------------------
        // Declare socket type datagram - UDP
        //----------------------------------------------------------------------
        case CSimpleSocket::SocketTypeUdp:
        {
            m_nSocketDomain = AF_INET;
            m_nSocketType = CSimpleSocket::SocketTypeUdp;
            break;
        }
        case CSimpleSocket::SocketTypeUdp6:
        {
            m_nSocketDomain = AF_INET6;
            m_nSocketType = CSimpleSocket::SocketTypeUdp6;
            break;
        }
	//----------------------------------------------------------------------
	// Declare socket type raw Ethernet - Ethernet
	//----------------------------------------------------------------------
	case CSimpleSocket::SocketTypeRaw:
	{
#ifdef _LINUX
	    m_nSocketDomain = AF_PACKET;
	    m_nSocketType = CSimpleSocket::SocketTypeRaw;
#endif
#ifdef WIN32
	    m_nSocketType = CSimpleSocket::SocketTypeInvalid;
#endif
	    break;
	}
	default:
	    m_nSocketType = CSimpleSocket::SocketTypeInvalid;
	    break;
    }
}

CSimpleSocket::CSimpleSocket(CSimpleSocket &socket)
{
    m_pBuffer = new UDK_U8[socket.m_nBufferSize];
	m_nBufferSize = socket.m_nBufferSize;
	memcpy(m_pBuffer, socket.m_pBuffer, socket.m_nBufferSize);
}

CSimpleSocket *CSimpleSocket::operator=(CSimpleSocket &socket)
{
	if (m_nBufferSize != socket.m_nBufferSize)
	{
		delete m_pBuffer;
		m_pBuffer = new UDK_U8[socket.m_nBufferSize];
		m_nBufferSize = socket.m_nBufferSize;
		memcpy(m_pBuffer, socket.m_pBuffer, socket.m_nBufferSize);
	}

	return this;
}



//------------------------------------------------------------------------------
//
// Initialize() - Initialize socket class
//
//------------------------------------------------------------------------------
bool CSimpleSocket::Initialize()
{
    errno = CSimpleSocket::SocketSuccess;

    //-------------------------------------------------------------------------
    // Create the basic Socket Handle										 
    //-------------------------------------------------------------------------
    m_timer.Initialize();
    m_timer.SetStartTime();
    m_socket = socket(m_nSocketDomain, m_nSocketType, 0);
    m_timer.SetEndTime();

	int set = 1;
	setsockopt(m_socket, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));  


    TranslateSocketError();

    return (IsSocketValid());
}


//------------------------------------------------------------------------------
//
// BindInterface()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::BindInterface(UDK_U8 *pInterface)
{
    bool           bRetVal = false;
    struct in_addr stInterfaceAddr;                                                                                           

    if (GetMulticast() == true)
    {
        if (pInterface)
        {
            stInterfaceAddr.s_addr= inet_addr((const char *)pInterface); 
            if (SETSOCKOPT(m_socket, IPPROTO_IP, IP_MULTICAST_IF, &stInterfaceAddr, sizeof(stInterfaceAddr)) == SocketSuccess)
            {
                bRetVal = true;
            }
        }
    }                          
    else
    {
	    SetSocketError(CSimpleSocket::SocketProtocolError);
    }

    return bRetVal;
}


//------------------------------------------------------------------------------
//
// SetMulticast()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetMulticast(bool bEnable, UDK_U8 multicastTTL)
{
    bool bRetVal = false;

    if (GetSocketType() == CSimpleSocket::SocketTypeUdp)
    {
        m_bIsMulticast = bEnable;
        if (SETSOCKOPT(m_socket, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&multicastTTL, sizeof(multicastTTL)) == SocketError)
        {
            TranslateSocketError();
            bRetVal = false;
        }
        else
        {
            bRetVal = true;
        }
    }
    else
    {
        m_socketErrno = CSimpleSocket::SocketProtocolError;
    }

    return bRetVal;
}

//------------------------------------------------------------------------------
//
// SetBroadcast()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetBroadcast(bool bEnable)
{
    bool bRetVal = false;

    if (GetSocketType() == CSimpleSocket::SocketTypeUdp)
    {
        m_bIsBroadcast = bEnable;
	const int on = 1;
        if (SETSOCKOPT(m_socket, SOL_SOCKET, SO_BROADCAST, (char *)&on, sizeof(on)) == SocketError)
        {
            TranslateSocketError();
            bRetVal = false;
        }
        else
        {
            bRetVal = true;
        }
    }
    else
    {
        m_socketErrno = CSimpleSocket::SocketProtocolError;
    }

    return bRetVal;
}
//------------------------------------------------------------------------------
//
// SetSocketDscp() 
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetSocketDscp(UDK_S32 nDscp)
{
    bool  bRetVal = true;
    UDK_S32 nTempVal = nDscp;

    nTempVal <<= 4;
    nTempVal /= 4;

    if (IsSocketValid())
    {
        if (SETSOCKOPT(m_socket, IPPROTO_IP, IP_TOS, &nTempVal, sizeof(nTempVal)) == SocketError)
        {
            TranslateSocketError();
            bRetVal = false;
        }
    }

    return bRetVal;
}

//------------------------------------------------------------------------------
//
// GetSocketDscp() 
//
//------------------------------------------------------------------------------
UDK_S32 CSimpleSocket::GetSocketDscp(void)
{
    UDK_S32      nTempVal = 0;
    socklen_t  nLen = 0; 

    if (IsSocketValid())
    {
        if (GETSOCKOPT(m_socket, IPPROTO_IP, IP_TOS, &nTempVal, &nLen) == SocketError)
        {
            TranslateSocketError();
        }

        nTempVal *= 4;
        nTempVal >>= 4;
    }

    return nTempVal;
}

//------------------------------------------------------------------------------
//
// GetWindowSize() 
//
//------------------------------------------------------------------------------
UDK_U32 CSimpleSocket::GetWindowSize(UDK_U32 nOptionName)
{
    UDK_U32 nTcpWinSize = 0;

    //-------------------------------------------------------------------------
    // no socket given, return system default allocate our own new socket
    //-------------------------------------------------------------------------
    if (m_socket != CSimpleSocket::SocketError)
    {
        socklen_t nLen = sizeof(nTcpWinSize);

        //---------------------------------------------------------------------
        // query for buffer size 
        //---------------------------------------------------------------------
        GETSOCKOPT(m_socket, SOL_SOCKET, nOptionName, &nTcpWinSize, &nLen);
        TranslateSocketError();
    }
    else
    {
        SetSocketError(CSimpleSocket::SocketInvalidSocket);
    }

    return nTcpWinSize;
}


//------------------------------------------------------------------------------
//
// SetWindowSize()
//
//------------------------------------------------------------------------------
UDK_U32 CSimpleSocket::SetWindowSize(UDK_U32 nOptionName, UDK_U32 nWindowSize)
{
    //-------------------------------------------------------------------------
    // no socket given, return system default allocate our own new socket
    //-------------------------------------------------------------------------
    if (m_socket != CSimpleSocket::SocketError)
    {
        SETSOCKOPT(m_socket, SOL_SOCKET, nOptionName, &nWindowSize, sizeof(nWindowSize));
        TranslateSocketError();
    }
    else
    {
        SetSocketError(CSimpleSocket::SocketInvalidSocket);
    }

    return (UDK_U32)nWindowSize;
}



//------------------------------------------------------------------------------
//
// DisableNagleAlgorithm()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::DisableNagleAlgoritm()
{
    bool  bRetVal = false;
    UDK_S32 nTcpNoDelay = 1;

    //----------------------------------------------------------------------
    // Set TCP NoDelay flag to true
    //----------------------------------------------------------------------
    if (SETSOCKOPT(m_socket, IPPROTO_TCP, TCP_NODELAY, &nTcpNoDelay, sizeof(UDK_S32)) == 0)
    {
        bRetVal = true;
    }

    TranslateSocketError();

    return bRetVal;
}


//------------------------------------------------------------------------------
//
// EnableNagleAlgorithm()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::EnableNagleAlgoritm()
{
    bool  bRetVal = false;
    UDK_S32 nTcpNoDelay = 0;

    //----------------------------------------------------------------------
    // Set TCP NoDelay flag to false
    //----------------------------------------------------------------------
    if (SETSOCKOPT(m_socket, IPPROTO_TCP, TCP_NODELAY, &nTcpNoDelay, sizeof(UDK_S32)) == 0)
    {
        bRetVal = true;
    }

    TranslateSocketError();

    return bRetVal;
}

//------------------------------------------------------------------------------
//
// Send() - Send data on a valid socket
//
//------------------------------------------------------------------------------
UDK_S32 CSimpleSocket::Send(const UDK_U8 *pBuf, size_t bytesToSend)
{
    SetSocketError(SocketSuccess);
    m_nBytesSent = 0;

    switch(m_nSocketType)
    {
        case CSimpleSocket::SocketTypeTcp:
        {
            if (IsSocketValid())
            {
                if ((bytesToSend > 0) && (pBuf != NULL))
                {
                    m_timer.Initialize();
                    m_timer.SetStartTime();
                    //---------------------------------------------------------
                    // Check error condition and attempt to resend if call
                    // was interrupted by a signal.
                    //---------------------------------------------------------
                    do
                    {
                        m_nBytesSent = SEND(m_socket, pBuf, bytesToSend, 0);

                        if (m_nBytesSent == SocketError)
                            TranslateSocketError();
                    } while (GetSocketError() == CSimpleSocket::SocketInterrupted);

                    m_timer.SetEndTime();
                }
            }
            break;
        }
        case CSimpleSocket::SocketTypeUdp:
        {
            if (IsSocketValid())
            {
                if ((bytesToSend > 0) && (pBuf != NULL))
                {
                    m_timer.Initialize();
                    m_timer.SetStartTime();

                    //---------------------------------------------------------
                    // Check error condition and attempt to resend if call
                    // was interrupted by a signal.
                   //---------------------------------------------------------
                    //                    if (GetMulticast())
                    //                    {
                    //                        do
                    //                        {
                    //                            m_nBytesSent = SENDTO(m_socket, pBuf, bytesToSend, 0, (const sockaddr *)&m_stMulticastGroup, 
                    //                                                  sizeof(m_stMulticastGroup));
                    //                            TranslateSocketError();
                    //                        } while (GetSocketError() == CSimpleSocket::SocketInterrupted);
                    //                    }
                    //                    else
                    {
                        do
                        {
                            m_nBytesSent = SENDTO(m_socket, pBuf, bytesToSend, 0, (const sockaddr *)&m_stServerSockaddr, sizeof(m_stServerSockaddr));
                            if (m_nBytesSent <= 0)
                                TranslateSocketError();
                        } while (GetSocketError() == CSimpleSocket::SocketInterrupted);
                    }

                    m_timer.SetEndTime();
                }
            }
            break;
        }
        default:
            break;
    }

    return m_nBytesSent;
}

//------------------------------------------------------------------------------
//
// Close() - Close socket and free up any memory allocated for the socket
//
//------------------------------------------------------------------------------
bool CSimpleSocket::Close(void)
{
	bool bRetVal = false;

	//--------------------------------------------------------------------------
	// delete internal buffer
	//--------------------------------------------------------------------------
	if (m_pBuffer != NULL)
	{
		delete [] m_pBuffer;
		m_pBuffer = NULL;
	}

	//--------------------------------------------------------------------------
	// if socket handle is currently valid, close and then invalidate
	//--------------------------------------------------------------------------
	if (IsSocketValid())
	{
		if (CLOSE(m_socket) != CSimpleSocket::SocketError)
		{
			m_socket = INVALID_SOCKET;
			bRetVal = true;
		}
        else
        {
            TranslateSocketError();
        }
	}

	return bRetVal;
}

//------------------------------------------------------------------------------
//
// Shtudown()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::Shutdown(CShutdownMode nShutdown)
{
	CSocketError nRetVal = SocketEunknown;

	nRetVal = (CSocketError)shutdown(m_socket, CSimpleSocket::Sends);

    if (nRetVal == SocketError)
        TranslateSocketError();

	return (nRetVal == CSimpleSocket::SocketSuccess) ? true: false;
}

//------------------------------------------------------------------------------
//
// Flush()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::Flush()
{
    UDK_S32 nTcpNoDelay = 1;
    UDK_S32 nCurFlags = 0;
    UDK_U8 tmpbuf = 0;
    bool  bRetVal = false;

    //--------------------------------------------------------------------------
    // Get the current setting of the TCP_NODELAY flag.
    //--------------------------------------------------------------------------
    if (GETSOCKOPT(m_socket, IPPROTO_TCP, TCP_NODELAY, &nCurFlags, sizeof(UDK_S32)) == 0)
    {
        //----------------------------------------------------------------------
        // Set TCP NoDelay flag
        //----------------------------------------------------------------------
        if (SETSOCKOPT(m_socket, IPPROTO_TCP, TCP_NODELAY, &nTcpNoDelay, sizeof(UDK_S32)) == 0)
        {
            //------------------------------------------------------------------
            // Send empty byte stream to flush the TCP send buffer
            //------------------------------------------------------------------
            if (Send(&tmpbuf, 0) != CSimpleSocket::SocketError)
            {
                bRetVal = true;
            }
            else
            {
                TranslateSocketError();
            }
        }

        //----------------------------------------------------------------------
        // Reset the TCP_NODELAY flag to original state.
        //----------------------------------------------------------------------
        SETSOCKOPT(m_socket, IPPROTO_TCP, TCP_NODELAY, &nCurFlags, sizeof(UDK_S32));
    }

    return bRetVal;
}

//------------------------------------------------------------------------------
//
// Writev -
//
//------------------------------------------------------------------------------
UDK_S32 CSimpleSocket::Writev(const struct iovec *pVector, size_t nCount)
{
    UDK_S32 nBytes     = 0;
    UDK_S32 nBytesSent = 0;
    UDK_S32 i          = 0;

    //--------------------------------------------------------------------------
    // Send each buffer as a separate send, windows does not support this
    // function call.                                                    
    //--------------------------------------------------------------------------
    for (i = 0; i < (UDK_S32)nCount; i++)
    {
        if ((nBytes = Send((UDK_U8 *)pVector[i].iov_base, pVector[i].iov_len)) == CSimpleSocket::SocketError)
        {
            break;
        }

        nBytesSent += nBytes;
    }
    
    if (i > 0)
    {
        Flush();
    }
    
    return nBytesSent;
}

//------------------------------------------------------------------------------
//
// Send() - Send data on a valid socket via a vector of buffers.
//
//------------------------------------------------------------------------------
UDK_S32 CSimpleSocket::Send(const struct iovec *sendVector, UDK_S32 nNumItems)
{
    SetSocketError(SocketSuccess);
    m_nBytesSent = 0;

    if ((m_nBytesSent = WRITEV(m_socket, sendVector, nNumItems)) == CSimpleSocket::SocketError)
    {
        TranslateSocketError();
    }

    return m_nBytesSent;
}

//------------------------------------------------------------------------------
//
// SetReceiveTimeout()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetReceiveTimeout(UDK_S32 nRecvTimeoutSec, UDK_S32 nRecvTimeoutUsec)
{
    bool bRetVal = true;

    memset(&m_stRecvTimeout, 0, sizeof(struct timeval));

    m_stRecvTimeout.tv_sec = nRecvTimeoutSec;
    m_stRecvTimeout.tv_usec = nRecvTimeoutUsec;

    //--------------------------------------------------------------------------
    // Sanity check to make sure the options are supported!					
    //--------------------------------------------------------------------------
    if (SETSOCKOPT(m_socket, SOL_SOCKET, SO_RCVTIMEO, &m_stRecvTimeout,
                   sizeof(struct timeval)) == CSimpleSocket::SocketError)
    {
        bRetVal = false;
        TranslateSocketError();
    }

    return bRetVal;
}

//------------------------------------------------------------------------------
//
// SetSendTimeout()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetSendTimeout(UDK_S32 nSendTimeoutSec, UDK_S32 nSendTimeoutUsec)
{
    bool bRetVal = true;

    memset(&m_stSendTimeout, 0, sizeof(struct timeval));
    m_stSendTimeout.tv_sec = nSendTimeoutSec;
    m_stSendTimeout.tv_usec = nSendTimeoutUsec;

    //--------------------------------------------------------------------------
    // Sanity check to make sure the options are supported!					
    //--------------------------------------------------------------------------
    if (SETSOCKOPT(m_socket, SOL_SOCKET, SO_SNDTIMEO, &m_stSendTimeout,
                   sizeof(struct timeval)) == CSimpleSocket::SocketError)
    {
        bRetVal = false;
        TranslateSocketError();
    }

    return bRetVal;
}

//------------------------------------------------------------------------------
//
// SetOptionReuseAddr()
//																			
//------------------------------------------------------------------------------
bool CSimpleSocket::SetOptionReuseAddr()
{
    bool  bRetVal = false;
    UDK_S32 nReuse  = IPTOS_LOWDELAY;

    if (SETSOCKOPT(m_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&nReuse, sizeof(UDK_S32)) == 0)
    {
        bRetVal = true;
    }
    else
    {
        TranslateSocketError();
    }

    return bRetVal;
}

//------------------------------------------------------------------------------
//
// SetOptionLinger()
//																			
//------------------------------------------------------------------------------
bool CSimpleSocket::SetOptionLinger(bool bEnable, UDK_U16 nTime)
{
    bool bRetVal = false;

    m_stLinger.l_onoff = (bEnable == true) ? 1: 0;
    m_stLinger.l_linger = nTime;

    if (SETSOCKOPT(m_socket, SOL_SOCKET, SO_LINGER, &m_stLinger, sizeof(m_stLinger)) == 0)
    {
        bRetVal = true;
    }
    else
    {
        TranslateSocketError();
    }

    return bRetVal;
}

//------------------------------------------------------------------------------
//
// Receive() - Attempts to receive a block of data on an established		
//			   connection.	Data is received in an internal buffer managed	
//			   by the class.  This buffer is only valid until the next call	
//			   to Receive(), a call to Close(), or until the object goes out
//			   of scope.													
//																			
//------------------------------------------------------------------------------
UDK_S32 CSimpleSocket::Receive(UDK_S32 nMaxBytes)
{
    m_nBytesReceived = 0;

    //--------------------------------------------------------------------------
    // If the socket is invalid then return false.
    //--------------------------------------------------------------------------
    if (IsSocketValid() == false)
    {
        return m_nBytesReceived;
    }

    //--------------------------------------------------------------------------
    // Free existing buffer and allocate a new buffer the size of
    // nMaxBytes.
    //--------------------------------------------------------------------------
    if ((m_pBuffer != NULL) && (nMaxBytes != m_nBufferSize))
    {
        delete [] m_pBuffer;
        m_pBuffer = NULL;
    }

    //--------------------------------------------------------------------------
    // Allocate a new internal buffer to receive data.
    //--------------------------------------------------------------------------
	if (m_pBuffer == NULL)
	{
		try
		{
			m_nBufferSize = nMaxBytes;
			m_pBuffer = new UDK_U8[nMaxBytes]; 
		}
		catch(std::bad_alloc)
		{
			m_pBuffer = NULL;
			m_nBufferSize=0;
			return m_nBytesReceived;
		}

	}

    SetSocketError(SocketSuccess);

    m_timer.Initialize();
    m_timer.SetStartTime();

    switch (m_nSocketType)
    {
        //----------------------------------------------------------------------
        // If zero bytes are received, then return.  If SocketERROR is 
        // received, free buffer and return CSocket::SocketError (-1) to caller.	
        //----------------------------------------------------------------------
        case CSimpleSocket::SocketTypeTcp:
        {
            do 
            {
                UDK_S32 nret = RECV(m_socket, (m_pBuffer + m_nBytesReceived), 
                                        nMaxBytes, m_nFlags);

                if (nret == SocketError)
                    TranslateSocketError();
                else
                    m_nBytesReceived += nret;
            } while ((GetSocketError() == CSimpleSocket::SocketInterrupted));

            break;
        }
        case CSimpleSocket::SocketTypeUdp:
        {
            UDK_U32 srcSize;
                
            srcSize = sizeof(struct sockaddr_in);

            if (GetMulticast() == true)
            {
                do 
                {
                    m_nBytesReceived = RECVFROM(m_socket, m_pBuffer, nMaxBytes, 0, 
                                                &m_stMulticastGroup, &srcSize);

                    if (m_nBytesReceived <= 0)
                        TranslateSocketError();
                } while (GetSocketError() == CSimpleSocket::SocketInterrupted);
            }
            else
            {
                do 
                {
                    m_nBytesReceived = RECVFROM(m_socket, m_pBuffer, nMaxBytes, 0, 
                                                &m_stClientSockaddr, &srcSize);

                    if (m_nBytesReceived <= 0)
                        TranslateSocketError();
                } while (GetSocketError() == CSimpleSocket::SocketInterrupted);
            }

            break;
        }
        default:
            break;
    }
    
    m_timer.SetEndTime();

    //--------------------------------------------------------------------------
    // If we encounter an error translate the error code and return.  One 
    // possible error code could be EAGAIN (EWOULDBLOCK) if the socket is
    // non-blocking.  This does not mean there is an error, but no data is
    // yet available on the socket.
    //--------------------------------------------------------------------------
    if (m_nBytesReceived == CSimpleSocket::SocketError)
    {
        if (m_pBuffer != NULL)
        {
            delete [] m_pBuffer;
            m_pBuffer = NULL;
        }
    }

    return m_nBytesReceived;
}

//------------------------------------------------------------------------------
//
// SetNonblocking()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetNonblocking(void)
{
    UDK_S32  nCurFlags;

    if ((nCurFlags = fcntl(m_socket, F_GETFL)) < 0)
    {
        TranslateSocketError();
        return false;
    }

    nCurFlags |= O_NONBLOCK;

    if (fcntl(m_socket, F_SETFL, nCurFlags) != 0)
    {
        TranslateSocketError();
        return false;
    }
    m_bIsBlocking = false;

    return true;
}

//------------------------------------------------------------------------------
//
// SetBlocking()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetBlocking(void)
{
    UDK_S32 nCurFlags;

    if ((nCurFlags = fcntl(m_socket, F_GETFL)) < 0)
    {
        TranslateSocketError();
        return false;
    }

    nCurFlags &= (~O_NONBLOCK);

    if (fcntl(m_socket, F_SETFL, nCurFlags) != 0)
    {
        TranslateSocketError();
        return false;
    }
    m_bIsBlocking = true;

    return true;
}
//------------------------------------------------------------------------------
//
// SendFile() - stands-in for system provided sendfile				
//
//------------------------------------------------------------------------------
UDK_S32 CSimpleSocket::SendFile(UDK_S32 nOutFd, UDK_S32 nInFd, off_t *pOffset, UDK_S32 nCount)
{
    UDK_S32  nOutCount = CSimpleSocket::SocketError;

    static char szData[SOCKET_SENDFILE_BLOCKSIZE];
    UDK_S32       nInCount = 0;

    if (LSEEK(nInFd, *pOffset, SEEK_SET) == -1)
    {
		return -1;
    }

    while (nOutCount < nCount)
    {
        nInCount = (nCount - nOutCount) < SOCKET_SENDFILE_BLOCKSIZE ? (nCount - nOutCount) : SOCKET_SENDFILE_BLOCKSIZE;

        if ((READ(nInFd, szData, nInCount)) != (UDK_S32)nInCount)
        {
            return -1;
        }

        if ((SEND(nOutFd, szData, nInCount, 0)) != (UDK_S32)nInCount)
        {
            return -1;
        }

        nOutCount += nInCount;
    }
		
    *pOffset += nOutCount;

    TranslateSocketError();

    return nOutCount;
}
 

//------------------------------------------------------------------------------
//
// TranslateSocketError() -					
//
//------------------------------------------------------------------------------
void CSimpleSocket::TranslateSocketError(void)
{
    switch (errno)
    {
        case EXIT_SUCCESS:
	    SetSocketError(CSimpleSocket::SocketSuccess);
            break;
        case ENOTCONN:
            SetSocketError(CSimpleSocket::SocketNotconnected);
            break;
        case ENOTSOCK:
        case EBADF:
        case EACCES:
        case EAFNOSUPPORT:
        case EMFILE:
        case ENFILE:
        case ENOBUFS:
        case ENOMEM:
        case EPROTONOSUPPORT:
	    SetSocketError(CSimpleSocket::SocketInvalidSocket);
            break;
        case ECONNREFUSED :
            SetSocketError(CSimpleSocket::SocketConnectionRefused);
            break;
        case ETIMEDOUT:
            SetSocketError(CSimpleSocket::SocketTimedout);
            break;
        case EINPROGRESS:
            SetSocketError(CSimpleSocket::SocketEinprogress);
            break;
        case EWOULDBLOCK:
            //		case EAGAIN:
	    SetSocketError(CSimpleSocket::SocketEwouldblock);
	    break;
        case EINTR:
            SetSocketError(CSimpleSocket::SocketInterrupted);
            break;
        case ECONNABORTED:
            SetSocketError(CSimpleSocket::SocketConnectionAborted);
            break;
        case EINVAL:
        case EPROTO:
            SetSocketError(CSimpleSocket::SocketProtocolError);
            break;
        case EPERM:
	    SetSocketError(CSimpleSocket::SocketFirewallError);
            break;
        case EFAULT:
	    SetSocketError(CSimpleSocket::SocketInvalidSocketBuffer);
            break;
        case ECONNRESET:
            SetSocketError(CSimpleSocket::SocketConnectionReset);
            break;
        case ENOPROTOOPT: 
            SetSocketError(CSimpleSocket::SocketConnectionReset);
            break;
        default:
            SetSocketError(CSimpleSocket::SocketEunknown);
            break;	
    }
}

//------------------------------------------------------------------------------
//
// Select()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::Select(UDK_S32 nTimeoutSec, UDK_S32 nTimeoutUSec)
{
    bool            bRetVal = false;
    struct timeval *pTimeout = NULL;
    struct timeval  timeout;
    UDK_S32           nNumDescriptors = -1;
    UDK_S32           nError = 0;

    FD_ZERO(&m_errorFds);
    FD_ZERO(&m_readFds);
    FD_ZERO(&m_writeFds);
    FD_SET(m_socket, &m_errorFds);
    FD_SET(m_socket, &m_readFds);
    FD_SET(m_socket, &m_writeFds);
    
    //---------------------------------------------------------------------
    // If timeout has been specified then set value, otherwise set timeout
    // to NULL which will block until a descriptor is ready for read/write
    // or an error has occurred.
    //---------------------------------------------------------------------
    if ((nTimeoutSec > 0) || (nTimeoutUSec > 0))
    {
        timeout.tv_sec = nTimeoutSec;
        timeout.tv_usec = nTimeoutUSec;
        pTimeout = &timeout;
    }
    
    nNumDescriptors = SELECT(m_socket+1, &m_readFds, &m_writeFds, &m_errorFds, pTimeout);
    
    //----------------------------------------------------------------------
    // Handle timeout
    //----------------------------------------------------------------------
    if (nNumDescriptors == 0) 
    {
        SetSocketError(CSimpleSocket::SocketTimedout);
    }
    //----------------------------------------------------------------------
    // If a file descriptor (read/write) is set then check the
    // socket error (SO_ERROR) to see if there is a pending error.
    //----------------------------------------------------------------------
    else if ((FD_ISSET(m_socket, &m_readFds)) || (FD_ISSET(m_socket, &m_writeFds)))
    {
        UDK_S32 nLen = sizeof(nError);
        
        if (GETSOCKOPT(m_socket, SOL_SOCKET, SO_ERROR, &nError, &nLen) == 0)
        {
            errno = nError;
            
            if (nError == 0)
            {
                bRetVal = true;
            }
        }
        else
        {
            TranslateSocketError();
        }
    }

    return bRetVal;
}

bool CSimpleSocket::Select(UDK_S32 nTimeoutSec, UDK_S32 nTimeoutUSec,int option){
	bool            bRetVal = false;
    struct timeval *pTimeout = NULL;
    struct timeval  timeout;
    UDK_S32           nNumDescriptors = -1;
    UDK_S32           nError = 0;

    FD_ZERO(&m_errorFds);
    FD_ZERO(&m_readFds);
    FD_ZERO(&m_writeFds);
    FD_SET(m_socket, &m_errorFds);
    FD_SET(m_socket, &m_readFds);
    FD_SET(m_socket, &m_writeFds);
    
    //---------------------------------------------------------------------
    // If timeout has been specified then set value, otherwise set timeout
    // to NULL which will block until a descriptor is ready for read/write
    // or an error has occurred.
    //---------------------------------------------------------------------
    if ((nTimeoutSec > 0) || (nTimeoutUSec > 0))
    {
        timeout.tv_sec = nTimeoutSec;
        timeout.tv_usec = nTimeoutUSec;
        pTimeout = &timeout;
    }
    if(!option){
    	nNumDescriptors = SELECT(m_socket+1, &m_readFds, &m_writeFds, &m_errorFds, pTimeout);
    }else {
    	nNumDescriptors = SELECT(m_socket+1, (option&0x1)?&m_readFds:NULL,
			(option&0x2)?&m_writeFds:NULL,
			(option&0x4)?&m_errorFds:NULL,
			pTimeout);
    }
    
    //----------------------------------------------------------------------
    // Handle timeout
    //----------------------------------------------------------------------
    if (nNumDescriptors == 0) 
    {
        SetSocketError(CSimpleSocket::SocketTimedout);
    }
    //----------------------------------------------------------------------
    // If a file descriptor (read/write) is set then check the
    // socket error (SO_ERROR) to see if there is a pending error.
    //----------------------------------------------------------------------
    else if ((FD_ISSET(m_socket, &m_readFds)) || (FD_ISSET(m_socket, &m_writeFds)))
    {
        UDK_S32 nLen = sizeof(nError);
        
        if (GETSOCKOPT(m_socket, SOL_SOCKET, SO_ERROR, &nError, &nLen) == 0)
        {
            errno = nError;
            
            if (nError == 0)
            {
                bRetVal = true;
            }
        }
        else
        {
            TranslateSocketError();
        }
    }

    return bRetVal;
}


